#include "stdafx.h"

namespace classifier_test
{

namespace high_order
{

high_order_classifier::high_order_classifier()
{
	Cross = 2;
	Cutter.reset(new high_order_concept_cutter);
	Clustering.reset(new high_order_concept_clustering);
	Cleaner.reset(new high_order_concept_cleaner);
	Predictor.reset(new high_order_stream_predictor);
}

high_order_classifier::high_order_classifier(const high_order_classifier& Val)
	: stream_classifier(Val)
{
	Cross = Val.Cross;
	Cutter = Val.Cutter->clone();
	Clustering = Val.Clustering->clone();
	Cleaner = Val.Cleaner->clone();
	Predictor = Val.Predictor->clone();
}

smart_ptr<stream_classifier> high_order_classifier::clone() const
{
	return make_smart_ptr(new high_order_classifier(*this));
}

void high_order_classifier::serialize(serializer& Serializer)
{
	stream_classifier::serialize(Serializer);
	Serializer
		("Cross", Cross)
		("Cutter", *Cutter)
		("Clustering", *Clustering)
		("Cleaner", *Cleaner)
		("Predictor", *Predictor);
}

concept_cutter* high_order_classifier::get_cutter() const
{
	return &*Cutter;
}

concept_clustering* high_order_classifier::get_clustering() const
{
	return &*Clustering;
}

concept_cleaner* high_order_classifier::get_cleaner() const
{
	return &*Cleaner;
}

stream_predictor* high_order_classifier::get_predictor() const
{
	return &*Predictor;
}

void high_order_classifier::build(subarray<const stream_record* const> Data)
{
	smart_ptr<validation_dataset> BaseVDataset;
	if (Cross < 0) BaseVDataset.reset(new holdout_validation_dataset);
	else BaseVDataset.reset(new cross_validation_dataset(Cross));

	smart_ptr<concept_cutter> TheCutter = Cutter->clone();
	TheCutter->set_base_classifier(&*BaseClassifier);
	TheCutter->set_base_validation_dataset(&*BaseVDataset);
	TheCutter->set_data(Data.convert<const record* const>());
	TheCutter->compute();

	smart_ptr<concept_clustering> TheClustering = Clustering->clone();
	TheClustering->set_base_classifier(&*BaseClassifier);
	TheClustering->set_chunk_count(TheCutter->get_chunk_count());
	for (int I = 0; I < TheCutter->get_chunk_count(); I++)
		TheClustering->set_chunk(I, TheCutter->get_chunk(I).vdataset, TheCutter->get_chunk(I).vclassifier, TheCutter->get_chunk(I).verror);
	TheClustering->compute();

	smart_ptr<concept_cleaner> TheCleaner = Cleaner->clone();
	TheCleaner->set_chunk_count(TheCutter->get_chunk_count());
	for (int I = 0; I < TheCutter->get_chunk_count(); I++)
		TheCleaner->set_chunk(I, TheCutter->get_chunk(I).begin, TheCutter->get_chunk(I).end, TheClustering->get_chunk(I).concept);
	TheCleaner->set_concept_count(TheClustering->get_concept_count());
	for (int I = 0; I < TheClustering->get_concept_count(); I++)
		TheCleaner->set_concept(I, TheClustering->get_concept(I).vdataset, TheClustering->get_concept(I).vclassifier, TheClustering->get_concept(I).verror);
	TheCleaner->compute();
	for (int I = 0; I < TheCleaner->get_concept_count(); I++)
		clogger()->finer("Concept #" + to_string(I) + ": " + to_string(TheCleaner->get_concept(I).vdataset->size()) + " " + to_string((float)TheCleaner->get_concept(I).verror));

	clogger()->fine("Building final classifiers");
	Predictor->set_domain(Domain);
	Predictor->set_base_classifier(&*BaseClassifier);
	Predictor->set_stream(Data.convert<const record* const>());
	Predictor->set_chunk_count(TheCleaner->get_chunk_count());
	for (int I = 0; I < TheCleaner->get_chunk_count(); I++)
		Predictor->set_chunk(I, TheCleaner->get_chunk(I).begin, TheCleaner->get_chunk(I).end, TheCleaner->get_chunk(I).concept);
	Predictor->set_concept_count(TheCleaner->get_concept_count());
	for (int I = 0; I < TheCleaner->get_concept_count(); I++)
		Predictor->set_concept(I, TheCleaner->get_concept(I).vdataset, TheCleaner->get_concept(I).vclassifier, TheCleaner->get_concept(I).verror);
	Predictor->compute();
}

void high_order_classifier::train(const stream_record& Record)
{
	Predictor->train(Record);
}

classifier_test::discr_value high_order_classifier::classify(const stream_record& Record) const
{
	return Predictor->classify(Record);
}

void high_order_classifier::estimate(const stream_record& Record, array<double>& Probs) const
{
	Predictor->estimate(Record, Probs);
}

int high_order_classifier::get_concept_count() const
{
	return Predictor->get_concept_count();
}

const array<double>& high_order_classifier::get_state_probabilities() const
{
	return Predictor->get_state_probabilities();
}

void high_order_classifier::initialize_options(command_line_options& Options) const
{
	stream_classifier::initialize_options(Options);
	Options.add("cross").set_argument("<count>").set_info("# of groups in cross validation, -1 denotes holdout validation, default 2");
	Cutter->initialize_options(Options);
	Clustering->initialize_options(Options);
	Cleaner->initialize_options(Options);
	Predictor->initialize_options(Options);
}

void high_order_classifier::apply_arguments(const command_line_arguments& Args)
{
	stream_classifier::apply_arguments(Args);
	Cross = to_int(Args["cross"].get_or_default("2"));
	Cutter->apply_arguments(Args);
	Clustering->apply_arguments(Args);
	Cleaner->apply_arguments(Args);
	Predictor->apply_arguments(Args);
}

}

}
